Loading the relevant libraries:
# for reading data and data carpentry
library(readr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(janitor)
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(units)
## udunits database from /usr/share/udunits/udunits2.xml
library(purrr)
# for handling spatial data
library(sf)
## Linking to GEOS 3.14.1, GDAL 3.12.1, PROJ 9.7.0; sf_use_s2() is TRUE
library(tidygeocoder)
library(crsuggest)
## Using the EPSG Dataset v10.019, a product of the International Association of Oil & Gas Producers.
## Please view the terms of use at https://epsg.org/terms-of-use.html.
# for mapping and visualization
library(leaflet)
library(RColorBrewer)
# for providing access to spatial data
library(osmdata)
## Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
Are alcohol outlets crime attractors or crime generators?
Reading in crime data:
crimes <- read_csv("data/data/2019-06-greater-manchester-street.csv")
## Rows: 32058 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): Crime ID, Month, Reported by, Falls within, Location, LSOA code, LS...
## dbl (2): Longitude, Latitude
## lgl (1): Context
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(crimes)
## Rows: 32,058
## Columns: 12
## $ `Crime ID` <chr> NA, "aa1cc4cb0c436f463635890bcb4ff2cba08f59925…
## $ Month <chr> "2019-06", "2019-06", "2019-06", "2019-06", "2…
## $ `Reported by` <chr> "Greater Manchester Police", "Greater Manchest…
## $ `Falls within` <chr> "Greater Manchester Police", "Greater Manchest…
## $ Longitude <dbl> -2.464422, -2.441166, -2.444807, -2.444807, -2…
## $ Latitude <dbl> 53.61250, 53.61604, 53.61151, 53.61151, 53.606…
## $ Location <chr> "On or near Parking Area", "On or near Pitcomb…
## $ `LSOA code` <chr> "E01004768", "E01004768", "E01004768", "E01004…
## $ `LSOA name` <chr> "Bolton 001A", "Bolton 001A", "Bolton 001A", "…
## $ `Crime type` <chr> "Anti-social behaviour", "Violence and sexual …
## $ `Last outcome category` <chr> NA, "Unable to prosecute suspect", "Unable to …
## $ Context <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
Cleaning the variable names using
janitor::clean_names():
crimes <- clean_names(crimes)
glimpse(crimes)
## Rows: 32,058
## Columns: 12
## $ crime_id <chr> NA, "aa1cc4cb0c436f463635890bcb4ff2cba08f599250b…
## $ month <chr> "2019-06", "2019-06", "2019-06", "2019-06", "201…
## $ reported_by <chr> "Greater Manchester Police", "Greater Manchester…
## $ falls_within <chr> "Greater Manchester Police", "Greater Manchester…
## $ longitude <dbl> -2.464422, -2.441166, -2.444807, -2.444807, -2.4…
## $ latitude <dbl> 53.61250, 53.61604, 53.61151, 53.61151, 53.60627…
## $ location <chr> "On or near Parking Area", "On or near Pitcombe …
## $ lsoa_code <chr> "E01004768", "E01004768", "E01004768", "E0100476…
## $ lsoa_name <chr> "Bolton 001A", "Bolton 001A", "Bolton 001A", "Bo…
## $ crime_type <chr> "Anti-social behaviour", "Violence and sexual of…
## $ last_outcome_category <chr> NA, "Unable to prosecute suspect", "Unable to pr…
## $ context <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
Reading in a geoJSON spatial file:
manchester_ward <- st_read("data/data/wards.geojson")
## Reading layer `wards' from data source
## `/home/norman/Documents/ThirdBrain/x1_Projects/RProjects/Notes-on-Crime-Mapping-and-Spatial-Data-Analysis-in-R/data/data/wards.geojson'
## using driver `GeoJSON'
## Simple feature collection with 215 features and 12 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 351664 ymin: 381168.6 xmax: 406087.5 ymax: 421039.8
## Projected CRS: OSGB36 / British National Grid
head(manchester_ward)
## Simple feature collection with 6 features and 12 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 351664 ymin: 394521.5 xmax: 369078.9 ymax: 407481
## Projected CRS: OSGB36 / British National Grid
## objectid wd16cd wd16nm wd16nmw lad16cd lad16nm bng_e bng_n
## 1 772 E05000851 Ince <NA> E08000010 Wigan 359959 405278
## 2 773 E05000852 Leigh East <NA> E08000010 Wigan 367298 400695
## 3 774 E05000853 Leigh South <NA> E08000010 Wigan 366260 398660
## 4 775 E05000854 Leigh West <NA> E08000010 Wigan 363428 400762
## 5 776 E05000855 Lowton East <NA> E08000010 Wigan 362642 397699
## 6 777 E05000856 Orrell <NA> E08000010 Wigan 353321 404083
## long lat st_areasha st_lengths geometry
## 1 -2.60570 53.54262 4780173 11442.63 POLYGON ((359198.5 403803.5...
## 2 -2.49447 53.50194 4972092 11056.20 POLYGON ((367700.6 401583.7...
## 3 -2.50990 53.48358 6521214 12377.97 POLYGON ((364490 397331.6, ...
## 4 -2.55282 53.50228 7315344 16159.92 POLYGON ((363107.9 399684.9...
## 5 -2.56431 53.47470 11185415 17990.73 POLYGON ((361792.3 394521.5...
## 6 -2.70568 53.53133 8884317 19650.85 POLYGON ((353418.8 407481, ...
Too much data! Filtering:
city_center <- manchester_ward |>
filter(wd16nm == "City Centre")
head(city_center)
## Simple feature collection with 1 feature and 12 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 382621 ymin: 397086.1 xmax: 385710.4 ymax: 399156.8
## Projected CRS: OSGB36 / British National Grid
## objectid wd16cd wd16nm wd16nmw lad16cd lad16nm bng_e bng_n
## 1 618 E05000697 City Centre <NA> E08000003 Manchester 383942 398119
## long lat st_areasha st_lengths geometry
## 1 -2.24342 53.47956 3102193 8569.712 POLYGON ((384111.6 399137.3...
Extracting the geometry data only using
st_geometry():
city_center_geometry <- st_geometry(city_center)
# quickly plot the geometry object
plot(city_center_geometry)
Plot of city center ward geometry
Getting the bounding box for our region of interest,
usingosmdata::getbb():
bb_sf <- osmdata::getbb(
place_name = "greater manchester united kingdom",
format_out = "sf_polygon"
)
head(bb_sf)
## Simple feature collection with 1 feature and 13 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -2.730521 ymin: 53.32731 xmax: -1.909622 ymax: 53.68572
## Geodetic CRS: WGS 84
## place_id
## 1 256958817
## licence
## 1 Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright
## osm_type osm_id lat lon class type place_rank
## 1 relation 88084 53.5065249 -2.3374401 boundary administrative 10
## importance addresstype name
## 1 0.6538459 state_district Greater Manchester
## display_name geometry
## 1 Greater Manchester, England, United Kingdom POLYGON ((-2.730521 53.5205...
# quickly plot the geometry object from `bb_sf`:
plot(st_geometry(bb_sf))
Plot of Greater Manchester boundary
Querying OSM for bars (the drinking establishment) within the Greater
Manchester Area using opq() (which stands for “overpass
query”):
osm_bar_sf <- osmdata::opq(bbox = bb_sf) |> # select bounding box
add_osm_feature(key = "amenity", value = "bar") |> # select features
osmdata_sf() # specify class
class(osm_bar_sf)
## [1] "osmdata_sf" "osmdata" "list"
Inspecting osm_bar_sf
head(osm_bar_sf)
## $bbox
## [1] "relation(id:88084)"
##
## $overpass_call
## [1] "[out:xml][timeout:25];\n(relation(id:88084); map_to_area->.searchArea; );\n(\n node [\"amenity\"=\"bar\"] (area.searchArea);\n way [\"amenity\"=\"bar\"] (area.searchArea);\n relation [\"amenity\"=\"bar\"] (area.searchArea);\n);\n(._;>;);\nout body;"
##
## $meta
## $meta$timestamp
## [1] "[ Tue 6 Jan 2026 16:09:29 ]"
##
## $meta$OSM_version
## [1] "0.6"
##
## $meta$overpass_version
## [1] "Overpass API 0.7.61.8 b1080abd"
##
##
## $osm_points
## Simple feature collection with 819 features and 118 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -2.704108 ymin: 53.35707 xmax: -2.005355 ymax: 53.64882
## Geodetic CRS: WGS 84
## First 10 features:
## osm_id name access addr:city addr:country
## 27024694 27024694 Didsbury Junction <NA> Manchester GB
## 30969134 30969134 The Grosvenor <NA> Manchester <NA>
## 30969136 30969136 Revolution <NA> Manchester GB
## 30969137 30969137 Joshua Brooks <NA> Manchester <NA>
## 268980212 268980212 Slug & Lettuce <NA> Manchester <NA>
## 293617257 293617257 Fifth Nightclub <NA> Manchester GB
## 324671296 324671296 Slug & Lettuce Deansgate <NA> Manchester <NA>
## 324696522 324696522 Cloud 23 <NA> <NA> <NA>
## 331413286 331413286 Slug and Lettuce <NA> Manchester <NA>
## 334648390 334648390 Revolución de Cuba <NA> Manchester GB
## addr:county addr:floor addr:housename addr:housenumber addr:place
## 27024694 <NA> <NA> <NA> 844-846 Didsbury
## 30969134 <NA> <NA> The Footage <NA> <NA>
## 30969136 <NA> <NA> <NA> 88-94 <NA>
## 30969137 <NA> <NA> <NA> 106 <NA>
## 268980212 <NA> <NA> <NA> 651 <NA>
## 293617257 <NA> <NA> <NA> 121 <NA>
## 324671296 <NA> <NA> <NA> 62 <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> Heron House 11-12 <NA>
## 334648390 <NA> <NA> Central Buildings 11 <NA>
## addr:postcode addr:state addr:street addr:subdistrict
## 27024694 M20 2RN <NA> Wilmslow Road <NA>
## 30969134 M1 7DZ <NA> Grosvenor Street <NA>
## 30969136 M1 5WH <NA> Oxford Street <NA>
## 30969137 M1 6NG <NA> Princess Street <NA>
## 268980212 M20 6QZ <NA> Wilmslow Road <NA>
## 293617257 M1 7AG <NA> Princess Street <NA>
## 324671296 M3 2EN <NA> Deansgate <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 M2 5HD <NA> Albert Square <NA>
## 334648390 M2 5QR <NA> Peter Street <NA>
## addr:suburb addr:town addr:unit air_conditioning alt_name amenity
## 27024694 <NA> <NA> <NA> <NA> <NA> bar
## 30969134 <NA> <NA> <NA> <NA> <NA> bar
## 30969136 <NA> <NA> <NA> <NA> <NA> bar
## 30969137 <NA> <NA> <NA> <NA> <NA> bar
## 268980212 <NA> <NA> <NA> <NA> <NA> bar
## 293617257 <NA> <NA> <NA> <NA> <NA> bar
## 324671296 <NA> <NA> <NA> <NA> <NA> bar
## 324696522 <NA> <NA> <NA> <NA> <NA> bar
## 331413286 <NA> <NA> <NA> <NA> <NA> bar
## 334648390 <NA> <NA> <NA> <NA> <NA> bar
## amenity_1 bar barrier branch brand brand:wikidata
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> Revolution Q64024398
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> Slug and Lettuce Q7542224
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> Slug and Lettuce Q7542224
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> Slug and Lettuce Q7542224
## 334648390 <NA> <NA> <NA> <NA> Revolución de Cuba Q64024691
## brand:wikipedia breakfast brewery building changing_table
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA>
## changing_table:count changing_table:location check_date
## 27024694 <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA>
## 30969136 <NA> <NA> 2025-08-14
## 30969137 <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA>
## check_date:opening_hours cocktails contact:email contact:facebook
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 2025-08-01 <NA> <NA> <NA>
## 268980212 <NA> yes <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> yes <NA> <NA>
## 324696522 <NA> yes <NA> <NA>
## 331413286 <NA> yes <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA>
## contact:instagram contact:phone contact:twitter contact:website
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA>
## craft_beer cuisine delivery description diet:vegan diet:vegetarian
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> yes
## 268980212 <NA> <NA> <NA> <NA> yes yes
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA> <NA>
## dog drive_in email entrance fhrs:authority fhrs:id
## 27024694 <NA> no <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> 1816309
## 30969136 <NA> <NA> <NA> <NA> <NA> 1820273
## 30969137 <NA> <NA> <NA> <NA> <NA> 1819583
## 268980212 <NA> <NA> <NA> <NA> <NA> 1818907
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA> 1820815
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA> 1818994
## 334648390 <NA> <NA> <NA> <NA> <NA> 1816812
## fhrs:local_authority_id fixme fixme:addr1 fixme:addr2 floor:material
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA>
## food foot height indoor_seating internet_access internet_access:fee
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 yes <NA> <NA> yes <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> <NA>
## 268980212 yes <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 yes <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 yes <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA> <NA>
## level lgbtq lgbtq:gay lgbtq:women live_music lunch microbrewery
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> yes <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## min_age not:addr:postcode not:operator:wikidata note note:name
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> Q7619176 <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA>
## old_name
## 27024694 <NA>
## 30969134 <NA>
## 30969136 <NA>
## 30969137 <NA>
## 268980212 <NA>
## 293617257 <NA>
## 324671296 <NA>
## 324696522 <NA>
## 331413286 <NA>
## 334648390 <NA>
## opening_hours
## 27024694 <NA>
## 30969134 <NA>
## 30969136 <NA>
## 30969137 <NA>
## 268980212 Mo-Th 12:00-22:00, Fr 12:00-24:00, Sa 10:00-01:00, Su 10:00-21:00
## 293617257 <NA>
## 324671296 <NA>
## 324696522 <NA>
## 331413286 <NA>
## 334648390 Mo-We 11:30-02:00; Th-Sa 11:30-03:00; Su 12:00-02:00
## opening_hours:signed operator operator:wikidata organic
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> Stonegate <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 no <NA> <NA> <NA>
## 268980212 <NA> Stonegate Q7619176 <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> Stonegate Q7619176 <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 <NA> Stonegate Q7619176 <NA>
## 334648390 <NA> <NA> <NA> <NA>
## outdoor_seating panoramax panoramax:0 payment:american_express
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 yes <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA>
## payment:cards payment:cash payment:credit_cards payment:debit_cards
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA>
## payment:mastercard payment:qr_code payment:visa phone real_ale
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> yes
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> yes
## 324696522 <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> yes
## 334648390 <NA> <NA> <NA> <NA> <NA>
## real_cider service:electricity smoking source source:addr sport
## 27024694 <NA> <NA> <NA> <NA> fhrs opendata <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> outside <NA> <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> outside <NA> <NA> <NA>
## start_date surveillance survey:date takeaway theme toilets
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA> yes
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA> <NA> <NA>
## toilets:access toilets:female toilets:unisex toilets:wheelchair
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 customers <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA>
## 324696522 <NA> <NA> <NA> <NA>
## 331413286 <NA> <NA> <NA> <NA>
## 334648390 <NA> <NA> <NA> <NA>
## website
## 27024694 <NA>
## 30969134 https://www.crafted-social.co.uk/footage-manchester
## 30969136 <NA>
## 30969137 <NA>
## 268980212 https://www.slugandlettuce.co.uk/didsbury
## 293617257 <NA>
## 324671296 https://www.slugandlettuce.co.uk/manchester-deansgate
## 324696522 <NA>
## 331413286 https://www.slugandlettuce.co.uk/manchester-albert-square
## 334648390 https://www.revoluciondecuba.com/bar/manchester
## website:booking wheelchair wheelchair:description wikidata wikipedia
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> no <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> no <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## 324671296 <NA> <NA> <NA> <NA> <NA>
## 324696522 <NA> yes <NA> <NA> <NA>
## 331413286 <NA> yes <NA> <NA> <NA>
## 334648390 <NA> yes <NA> <NA> <NA>
## geometry
## 27024694 POINT (-2.23097 53.41074)
## 30969134 POINT (-2.236885 53.47016)
## 30969136 POINT (-2.240423 53.47363)
## 30969137 POINT (-2.23742 53.47425)
## 268980212 POINT (-2.231578 53.4191)
## 293617257 POINT (-2.23759 53.47497)
## 324671296 POINT (-2.246766 53.48295)
## 324696522 POINT (-2.250193 53.47549)
## 331413286 POINT (-2.246106 53.47924)
## 334648390 POINT (-2.248952 53.47832)
##
## $osm_lines
## NULL
##
## $osm_polygons
## Simple feature collection with 72 features and 68 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -2.704108 ymin: 53.35707 xmax: -2.005355 ymax: 53.64882
## Geodetic CRS: WGS 84
## First 10 features:
## osm_id name access addr:city addr:country
## 31894775 31894775 Common <NA> Manchester <NA>
## 77808365 77808365 Squirrels Bar <NA> <NA> <NA>
## 136006267 136006267 Fifteens <NA> Wigan <NA>
## 183275512 183275512 Folk Cafe Bar <NA> Manchester <NA>
## 183305127 183305127 Volta <NA> Manchester <NA>
## 183750765 183750765 HAUS <NA> <NA> <NA>
## 205672328 205672328 The Witchwood <NA> Ashton-under-Lyne <NA>
## 231022304 231022304 The Crooked Man <NA> <NA> <NA>
## 231804597 231804597 Casa De Moor <NA> Stockport <NA>
## 231833075 231833075 Heaton Social <NA> Stockport <NA>
## addr:housename addr:housenumber addr:postcode addr:province
## 31894775 <NA> 39-41 M4 1HW Lancashire
## 77808365 <NA> <NA> <NA> <NA>
## 136006267 <NA> 73 WN6 0HD <NA>
## 183275512 <NA> 169-171 M20 2LN <NA>
## 183305127 <NA> 167 M20 2LN <NA>
## 183750765 <NA> 200 M20 2LW <NA>
## 205672328 <NA> 152 OL6 7SF <NA>
## 231022304 <NA> 7 <NA> <NA>
## 231804597 <NA> 60 SK4 4NZ <NA>
## 231833075 <NA> 3 SK4 4AG <NA>
## addr:street addr:subdistrict addr:suburb amenity brand
## 31894775 Edge Street <NA> <NA> bar <NA>
## 77808365 <NA> <NA> <NA> bar <NA>
## 136006267 High Street Standish with Langtree Standish bar <NA>
## 183275512 Burton Road <NA> <NA> bar <NA>
## 183305127 Burton Road <NA> <NA> bar <NA>
## 183750765 Burton Road <NA> <NA> bar <NA>
## 205672328 Old Street <NA> <NA> bar <NA>
## 231022304 Fairfax Road <NA> <NA> bar <NA>
## 231804597 Heaton Moor Road <NA> <NA> bar <NA>
## 231833075 Shaw Road <NA> Heaton Moor bar <NA>
## brand:wikidata brewery building building:levels building:min_level
## 31894775 <NA> <NA> yes <NA> <NA>
## 77808365 <NA> <NA> yes <NA> <NA>
## 136006267 <NA> <NA> yes <NA> <NA>
## 183275512 <NA> <NA> yes <NA> <NA>
## 183305127 <NA> <NA> yes <NA> <NA>
## 183750765 <NA> <NA> yes <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> yes <NA> <NA>
## 231804597 <NA> <NA> yes <NA> <NA>
## 231833075 <NA> <NA> yes <NA> <NA>
## building:name building:part building:use check_date
## 31894775 <NA> <NA> <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> <NA>
## 183305127 <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA>
## check_date:opening_hours cocktails contact:facebook contact:instagram
## 31894775 <NA> yes <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> <NA>
## 183305127 <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA>
## 231804597 2025-10-23 <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA>
## contact:phone contact:website craft_beer
## 31894775 <NA> <NA> yes
## 77808365 <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA>
## 183275512 +44 161 445 2912 http://www.folkcafebar.co.uk/ <NA>
## 183305127 +44 1614 488887 http://www.rhubarbrestaurant.co.uk <NA>
## 183750765 <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA>
## cuisine description email fhrs:id food indoor_seating
## 31894775 <NA> <NA> <NA> <NA> yes <NA>
## 77808365 <NA> <NA> <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> 1818122 yes <NA>
## 183305127 british <NA> <NA> 1819076 <NA> <NA>
## 183750765 <NA> <NA> <NA> 1822049 yes <NA>
## 205672328 <NA> <NA> <NA> 1035549 <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA> <NA> yes <NA>
## 231833075 <NA> <NA> palemoor1@outlook.com 1065743 <NA> <NA>
## internet_access level live_music min_age music old_fhrs:id old_name
## 31894775 wlan <NA> <NA> <NA> <NA> <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> <NA> <NA> 756000 <NA>
## 183305127 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## opening_hours
## 31894775 <NA>
## 77808365 <NA>
## 136006267 <NA>
## 183275512 Tu-Su 10:00-23:00
## 183305127 Mo off; Tu-Th 17:00-21:30; Fr-Sa 17:00-22:00; Su 13:00-21:00
## 183750765 Mo-Su 09:00-01:00
## 205672328 <NA>
## 231022304 <NA>
## 231804597 <NA>
## 231833075 Tu-Su 16:00-00:00
## opening_hours:signed operator operator:wikidata outdoor_seating owner
## 31894775 <NA> <NA> <NA> <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> yes <NA>
## 183305127 <NA> <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> yes <NA>
## 205672328 <NA> <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA> <NA>
## 231804597 no <NA> <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA> <NA>
## owner:wikidata payment:cash payment:credit_cards payment:debit_cards
## 31894775 <NA> <NA> <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> <NA>
## 183305127 <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA>
## phone real_ale ref:GB:uprn
## 31894775 <NA> yes <NA>
## 77808365 <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA>
## 183305127 <NA> <NA> 10091812371;77092833
## 183750765 <NA> yes <NA>
## 205672328 +44 161 344 0321 <NA> <NA>
## 231022304 <NA> <NA> 100012688897
## 231804597 <NA> <NA> <NA>
## 231833075 <NA> <NA> 100011519690;100012782121;100011519691
## roof:levels roof:material smoking source source:addr source:name
## 31894775 <NA> <NA> <NA> <NA> <NA> <NA>
## 77808365 <NA> <NA> <NA> <NA> <NA> <NA>
## 136006267 <NA> <NA> <NA> <NA> <NA> <NA>
## 183275512 <NA> <NA> <NA> <NA> <NA> <NA>
## 183305127 <NA> <NA> <NA> <NA> <NA> <NA>
## 183750765 <NA> <NA> <NA> <NA> <NA> <NA>
## 205672328 <NA> <NA> <NA> <NA> <NA> <NA>
## 231022304 <NA> <NA> <NA> <NA> <NA> <NA>
## 231804597 <NA> <NA> <NA> <NA> <NA> <NA>
## 231833075 <NA> <NA> <NA> <NA> <NA> <NA>
## toilets:wheelchair twitter
## 31894775 <NA> <NA>
## 77808365 <NA> <NA>
## 136006267 <NA> <NA>
## 183275512 <NA> <NA>
## 183305127 <NA> <NA>
## 183750765 <NA> <NA>
## 205672328 <NA> <NA>
## 231022304 <NA> <NA>
## 231804597 <NA> <NA>
## 231833075 <NA> https://twitter.com/paleheatonmoor
## website wheelchair wheelchair:description
## 31894775 <NA> limited <NA>
## 77808365 <NA> <NA> <NA>
## 136006267 <NA> limited <NA>
## 183275512 <NA> yes <NA>
## 183305127 <NA> <NA> <NA>
## 183750765 https://www.hausdidsbury.com/ <NA> <NA>
## 205672328 http://thewitchwood.net <NA> <NA>
## 231022304 <NA> <NA> <NA>
## 231804597 https://casademoor.com/heaton-moor/ <NA> <NA>
## 231833075 <NA> <NA> <NA>
## wikidata geometry
## 31894775 <NA> POLYGON ((-2.235982 53.4844...
## 77808365 <NA> POLYGON ((-2.216829 53.4434...
## 136006267 <NA> POLYGON ((-2.661205 53.5848...
## 183275512 <NA> POLYGON ((-2.242833 53.4263...
## 183305127 <NA> POLYGON ((-2.242687 53.4265...
## 183750765 <NA> POLYGON ((-2.242799 53.4269...
## 205672328 Q7775324 POLYGON ((-2.099437 53.4862...
## 231022304 <NA> POLYGON ((-2.285096 53.5339...
## 231804597 <NA> POLYGON ((-2.182471 53.4236...
## 231833075 <NA> POLYGON ((-2.184632 53.4228...
Saving only the points:
osm_bar_sf <- osm_bar_sf$osm_points
head(osm_bar_sf)
## Simple feature collection with 6 features and 118 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -2.240422 ymin: 53.41074 xmax: -2.23097 ymax: 53.47497
## Geodetic CRS: WGS 84
## osm_id name access addr:city addr:country
## 27024694 27024694 Didsbury Junction <NA> Manchester GB
## 30969134 30969134 The Grosvenor <NA> Manchester <NA>
## 30969136 30969136 Revolution <NA> Manchester GB
## 30969137 30969137 Joshua Brooks <NA> Manchester <NA>
## 268980212 268980212 Slug & Lettuce <NA> Manchester <NA>
## 293617257 293617257 Fifth Nightclub <NA> Manchester GB
## addr:county addr:floor addr:housename addr:housenumber addr:place
## 27024694 <NA> <NA> <NA> 844-846 Didsbury
## 30969134 <NA> <NA> The Footage <NA> <NA>
## 30969136 <NA> <NA> <NA> 88-94 <NA>
## 30969137 <NA> <NA> <NA> 106 <NA>
## 268980212 <NA> <NA> <NA> 651 <NA>
## 293617257 <NA> <NA> <NA> 121 <NA>
## addr:postcode addr:state addr:street addr:subdistrict
## 27024694 M20 2RN <NA> Wilmslow Road <NA>
## 30969134 M1 7DZ <NA> Grosvenor Street <NA>
## 30969136 M1 5WH <NA> Oxford Street <NA>
## 30969137 M1 6NG <NA> Princess Street <NA>
## 268980212 M20 6QZ <NA> Wilmslow Road <NA>
## 293617257 M1 7AG <NA> Princess Street <NA>
## addr:suburb addr:town addr:unit air_conditioning alt_name amenity
## 27024694 <NA> <NA> <NA> <NA> <NA> bar
## 30969134 <NA> <NA> <NA> <NA> <NA> bar
## 30969136 <NA> <NA> <NA> <NA> <NA> bar
## 30969137 <NA> <NA> <NA> <NA> <NA> bar
## 268980212 <NA> <NA> <NA> <NA> <NA> bar
## 293617257 <NA> <NA> <NA> <NA> <NA> bar
## amenity_1 bar barrier branch brand brand:wikidata
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> Revolution Q64024398
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> Slug and Lettuce Q7542224
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## brand:wikipedia breakfast brewery building changing_table
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## changing_table:count changing_table:location check_date
## 27024694 <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA>
## 30969136 <NA> <NA> 2025-08-14
## 30969137 <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA>
## check_date:opening_hours cocktails contact:email contact:facebook
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 2025-08-01 <NA> <NA> <NA>
## 268980212 <NA> yes <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## contact:instagram contact:phone contact:twitter contact:website
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## craft_beer cuisine delivery description diet:vegan diet:vegetarian
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> yes
## 268980212 <NA> <NA> <NA> <NA> yes yes
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## dog drive_in email entrance fhrs:authority fhrs:id
## 27024694 <NA> no <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> 1816309
## 30969136 <NA> <NA> <NA> <NA> <NA> 1820273
## 30969137 <NA> <NA> <NA> <NA> <NA> 1819583
## 268980212 <NA> <NA> <NA> <NA> <NA> 1818907
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## fhrs:local_authority_id fixme fixme:addr1 fixme:addr2 floor:material
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## food foot height indoor_seating internet_access internet_access:fee
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 yes <NA> <NA> yes <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> <NA>
## 268980212 yes <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## level lgbtq lgbtq:gay lgbtq:women live_music lunch microbrewery
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> yes <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA> <NA>
## min_age not:addr:postcode not:operator:wikidata note note:name
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> Q7619176 <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## old_name
## 27024694 <NA>
## 30969134 <NA>
## 30969136 <NA>
## 30969137 <NA>
## 268980212 <NA>
## 293617257 <NA>
## opening_hours
## 27024694 <NA>
## 30969134 <NA>
## 30969136 <NA>
## 30969137 <NA>
## 268980212 Mo-Th 12:00-22:00, Fr 12:00-24:00, Sa 10:00-01:00, Su 10:00-21:00
## 293617257 <NA>
## opening_hours:signed operator operator:wikidata organic
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> Stonegate <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 no <NA> <NA> <NA>
## 268980212 <NA> Stonegate Q7619176 <NA>
## 293617257 <NA> <NA> <NA> <NA>
## outdoor_seating panoramax panoramax:0 payment:american_express
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 yes <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## payment:cards payment:cash payment:credit_cards payment:debit_cards
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## payment:mastercard payment:qr_code payment:visa phone real_ale
## 27024694 <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA> yes
## 268980212 <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA>
## real_cider service:electricity smoking source source:addr sport
## 27024694 <NA> <NA> <NA> <NA> fhrs opendata <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> outside <NA> <NA> <NA>
## 30969137 yes <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## start_date surveillance survey:date takeaway theme toilets
## 27024694 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA> <NA> yes
## 30969137 <NA> <NA> <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA> <NA> <NA>
## toilets:access toilets:female toilets:unisex toilets:wheelchair
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 <NA> <NA> <NA> <NA>
## 30969136 customers <NA> <NA> <NA>
## 30969137 <NA> <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## website website:booking
## 27024694 <NA> <NA>
## 30969134 https://www.crafted-social.co.uk/footage-manchester <NA>
## 30969136 <NA> <NA>
## 30969137 <NA> <NA>
## 268980212 https://www.slugandlettuce.co.uk/didsbury <NA>
## 293617257 <NA> <NA>
## wheelchair wheelchair:description wikidata wikipedia
## 27024694 <NA> <NA> <NA> <NA>
## 30969134 no <NA> <NA> <NA>
## 30969136 <NA> <NA> <NA> <NA>
## 30969137 no <NA> <NA> <NA>
## 268980212 <NA> <NA> <NA> <NA>
## 293617257 <NA> <NA> <NA> <NA>
## geometry
## 27024694 POINT (-2.23097 53.41074)
## 30969134 POINT (-2.236885 53.47016)
## 30969136 POINT (-2.240423 53.47363)
## 30969137 POINT (-2.23742 53.47425)
## 268980212 POINT (-2.231578 53.4191)
## 293617257 POINT (-2.23759 53.47497)
Too much data! Filtering to exclude blanks
osm_bar_sf <- osm_bar_sf |> filter(!is.na(name))
dim(osm_bar_sf)
## [1] 325 119
We are still left with 325 bars in our data.
The function filter() is an attribute operation because
it enabled us to make changes to the data by manipulating elements in
the attribute table.
Further filtering the data to include only a particular violent crime:
unique(crimes$crime_type)
## [1] "Anti-social behaviour" "Violence and sexual offences"
## [3] "Criminal damage and arson" "Other theft"
## [5] "Vehicle crime" "Burglary"
## [7] "Public order" "Shoplifting"
## [9] "Other crime" "Possession of weapons"
## [11] "Theft from the person" "Bicycle theft"
## [13] "Robbery" "Drugs"
crimes <- crimes |>
filter(crime_type == "Violence and sexual offences")
Checking the CRS of our crimes data:
st_crs(crimes)
## Coordinate Reference System: NA
There is no CRS because crimes data is not a spatial
object (yet)
Converting crimes to a spatial object:
crimes <- crimes |> st_as_sf(
coords = c("longitude", "latitude"),
crs = 4326,
agr = "constant",
na.fail = FALSE
)
Here are the attributes for the agr parameter: -
"constant": attributes that are constant throughout the
geometry (like land use).
- "aggregate": used when the attribute is an aggregate
value (like population count or population density).
- "identity": used when the attributes uniquely identifies
the geometry of the the object we are interested in, like building ID,
or city name.
Checking the CRS of crime once again:
st_crs(crimes)
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)"],
## MEMBER["World Geodetic System 1984 (G730)"],
## MEMBER["World Geodetic System 1984 (G873)"],
## MEMBER["World Geodetic System 1984 (G1150)"],
## MEMBER["World Geodetic System 1984 (G1674)"],
## MEMBER["World Geodetic System 1984 (G1762)"],
## MEMBER["World Geodetic System 1984 (G2139)"],
## MEMBER["World Geodetic System 1984 (G2296)"],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]],
## ENSEMBLEACCURACY[2.0]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## USAGE[
## SCOPE["Horizontal component of 3D system."],
## AREA["World."],
## BBOX[-90,-180,90,180]],
## ID["EPSG",4326]]
Checking the CRS of city_center:
st_crs(city_center)
## Coordinate Reference System:
## User input: OSGB36 / British National Grid
## wkt:
## PROJCRS["OSGB36 / British National Grid",
## BASEGEOGCRS["OSGB36",
## DATUM["Ordnance Survey of Great Britain 1936",
## ELLIPSOID["Airy 1830",6377563.396,299.3249646,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## ID["EPSG",4277]],
## CONVERSION["British National Grid",
## METHOD["Transverse Mercator",
## ID["EPSG",9807]],
## PARAMETER["Latitude of natural origin",49,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8801]],
## PARAMETER["Longitude of natural origin",-2,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8802]],
## PARAMETER["Scale factor at natural origin",0.9996012717,
## SCALEUNIT["unity",1],
## ID["EPSG",8805]],
## PARAMETER["False easting",400000,
## LENGTHUNIT["metre",1],
## ID["EPSG",8806]],
## PARAMETER["False northing",-100000,
## LENGTHUNIT["metre",1],
## ID["EPSG",8807]]],
## CS[Cartesian,2],
## AXIS["(E)",east,
## ORDER[1],
## LENGTHUNIT["metre",1]],
## AXIS["(N)",north,
## ORDER[2],
## LENGTHUNIT["metre",1]],
## USAGE[
## SCOPE["Engineering survey, topographic mapping."],
## AREA["United Kingdom (UK) - offshore to boundary of UKCS within 49°45'N to 61°N and 9°W to 2°E; onshore Great Britain (England, Wales and Scotland). Isle of Man onshore."],
## BBOX[49.75,-9.01,61.01,2.01]],
## ID["EPSG",27700]]
The city_center uses a different CRS.
Re-projecting the CRS of city_center to WGS84 using
st_transform():
city_center <- st_transform(city_center, crs = 4326)
Checking if the projections are now similar:
st_crs(crimes) == st_crs(city_center)
## [1] TRUE
Checking the CRS of the osm_bar_sf data from Open Street
Map:
st_crs(osm_bar_sf)
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)"],
## MEMBER["World Geodetic System 1984 (G730)"],
## MEMBER["World Geodetic System 1984 (G873)"],
## MEMBER["World Geodetic System 1984 (G1150)"],
## MEMBER["World Geodetic System 1984 (G1674)"],
## MEMBER["World Geodetic System 1984 (G1762)"],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]],
## ENSEMBLEACCURACY[2.0]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## USAGE[
## SCOPE["Horizontal component of 3D system."],
## AREA["World."],
## BBOX[-90,-180,90,180]],
## ID["EPSG",4326]]
Now all data sources have matching CRS!
Plotting osm_bar_sf and city_center
geometries:
plot(st_geometry(osm_bar_sf), col = "red")
plot(st_geometry(city_center), add = TRUE)
May bars fall outside the City Center ward
Plotting osm_bar_sf, crimes, and
city_center geometries:
plot(st_geometry(osm_bar_sf), col = "red")
plot(st_geometry(crimes), col = "blue", add = TRUE)
plot(st_geometry(city_center), add = TRUE)
Most crimes fall outside the City Center ward
Getting the intersection of city_center and
crimes geometries:
cc_crimes <- st_intersects(city_center, crimes)
Subsetting crimes using the result of
st_intersects():
cc_crimes <- crimes[unlist(cc_crimes),]
head(cc_crimes)
## Simple feature collection with 6 features and 10 fields
## Attribute-geometry relationships: constant (10)
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -2.248958 ymin: 53.48214 xmax: -2.238637 ymax: 53.48675
## Geodetic CRS: WGS 84
## # A tibble: 6 × 11
## crime_id month reported_by falls_within location lsoa_code lsoa_name
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 5a4de7fc0da3294db… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 2 98f7f39a9437d17ca… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 3 e8c5555dc4a9b9fdc… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 4 6c1bc711333e498f1… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 5 a42d88a2f8e9d9b87… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 6 ffe16234ce35bbc91… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## # ℹ 4 more variables: crime_type <chr>, last_outcome_category <chr>,
## # context <lgl>, geometry <POINT [°]>
Plotting city_center and cc_crimes
geometries:
plot(st_geometry(city_center))
plot(st_geometry(cc_crimes), col = "blue", add = TRUE)
Crimes inside the City Center ward
Applying the same concept for the bars in Manchester:
cc_bars <- st_intersects(city_center, osm_bar_sf)
cc_bars <- osm_bar_sf[unlist(cc_bars), ]
Plotting the three geometries again:
plot(st_geometry(city_center))
plot(st_geometry(cc_bars), col = "red", add = TRUE)
plot(st_geometry(cc_crimes), col = "blue", add = TRUE)
Bars and crimes within the City Center
How do we connect the crimes to the bars then?
One approach to answer the question above is to build a buffer around the bars, and count all the crimes that fall within a specific radius of this bar.
Trying the st_buffer() function:
prem_buffer <- st_buffer(cc_bars, 1)
This works but it does not make much sense since the current CRS uses “degrees” instead of direct length units. The function created buffers of 1 degree around the bars.
Transforming the CRS of bars to BNG (British National Grid) first, before buffering with a radius of 400m:
bars_bng <- st_transform(cc_bars, crs = 27700) # code for BNG is 27700
bars_buffer <- st_buffer(bars_bng, 400)
Plotting:
plot(st_geometry(bars_buffer))
plot(st_geometry(bars_bng), add = TRUE)
Bars with 400m buffers
There are a lot of overlaps here, since there are many bars in the area. Choosing a buffering radius of 100 meters:
bar_buffer_100 <- st_buffer(bars_bng, 100)
# plotting
plot(st_geometry(bar_buffer_100))
plot(st_geometry(bars_bng), add = TRUE)
Transforming the buffer data back to WGS84:
buffer_WGS84 <- st_transform(bar_buffer_100, crs = 4326)
Plotting once more:
plot(st_geometry(buffer_WGS84))
plot(st_geometry(cc_crimes), col = "blue", add = TRUE)
Crimes around the 100m buffer polygons
How many crimes occur within the buffer polygons?
In computational geometry, the question above falls under the point-in-polygon problem category.
Joining the buffer data to cc_crimes data and creating a
frequency table of the number of crimes within the buffer of each
bar:
crimes_per_prem <- st_join(buffer_WGS84, cc_crimes, left = FALSE) |>
count(name)
head(crimes_per_prem)
## Simple feature collection with 6 features and 2 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -2.251548 ymin: 53.47374 xmax: -2.232572 ymax: 53.4832
## Geodetic CRS: WGS 84
## name n geometry
## 1 33 21 POLYGON ((-2.23473 53.48235...
## 2 ARK 1 POLYGON ((-2.248537 53.4746...
## 3 Albert's Schloss 13 POLYGON ((-2.246406 53.4782...
## 4 All Bar One 3 POLYGON ((-2.241114 53.481,...
## 5 Allotment 23 POLYGON ((-2.232574 53.4816...
## 6 Arcane Bar 9 POLYGON ((-2.245908 53.4810...
Inspecting which premises have the most violent crimes:
crimes_per_prem |>
select(name, n) |>
arrange(desc(n))
## Simple feature collection with 115 features and 2 fields
## Geometry type: GEOMETRY
## Dimension: XY
## Bounding box: xmin: -2.25777 ymin: 53.47262 xmax: -2.220188 ymax: 53.48615
## Geodetic CRS: WGS 84
## First 10 features:
## name n geometry
## 1 Crafty Pig 59 POLYGON ((-2.234923 53.4819...
## 2 Centre Stage 32 POLYGON ((-2.236641 53.4769...
## 3 Dive 31 POLYGON ((-2.234829 53.4822...
## 4 G-A-Y 30 POLYGON ((-2.236429 53.4765...
## 5 On Bar Manchester 30 POLYGON ((-2.236242 53.4766...
## 6 NQ64 25 MULTIPOLYGON (((-2.246766 5...
## 7 Mala 24 POLYGON ((-2.233471 53.4820...
## 8 Night & Day 24 POLYGON ((-2.233702 53.4827...
## 9 Allotment 23 POLYGON ((-2.232574 53.4816...
## 10 Noho 22 POLYGON ((-2.232927 53.4826...
Since there are overlapping buffer regions, there might be plenty of double-counting here.
To overcome this double-counting issue, we can assign each crime point to the closest possible bar.
Getting the nearest bar to crime data point using
st_nearest_feature():
crime_w_bars <- cc_crimes |>
mutate(nearest_bar = cc_bars[st_nearest_feature(cc_crimes, cc_bars), 2])
head(crime_w_bars)
## Simple feature collection with 6 features and 11 fields
## Attribute-geometry relationships: constant (10), NA's (1)
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -2.248958 ymin: 53.48214 xmax: -2.238637 ymax: 53.48675
## Geodetic CRS: WGS 84
## # A tibble: 6 × 12
## crime_id month reported_by falls_within location lsoa_code lsoa_name
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 5a4de7fc0da3294db… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 2 98f7f39a9437d17ca… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 3 e8c5555dc4a9b9fdc… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 4 6c1bc711333e498f1… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 5 a42d88a2f8e9d9b87… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## 6 ffe16234ce35bbc91… 2019… Greater Ma… Greater Man… On or n… E01033658 Manchest…
## # ℹ 5 more variables: crime_type <chr>, last_outcome_category <chr>,
## # context <lgl>, geometry <POINT [°]>, nearest_bar <sf[,2]>
Removing the geometry data at this point:
crimes_per_prem_2 <- crime_w_bars |>
st_drop_geometry() |>
group_by(nearest_bar$name) |>
summarize(num_crimes = n()) |>
rename(name = `nearest_bar$name`)
head(crimes_per_prem_2)
## # A tibble: 6 × 2
## name num_crimes
## <chr> <int>
## 1 33 3
## 2 Albert's Schloss 10
## 3 All Bar One 1
## 4 Allotment 1
## 5 Arcane Bar 7
## 6 Area 2
Joining this data to cc_bars:
crimes_per_prem_2 <-left_join(
cc_bars, crimes_per_prem_2,
by = c("name" = "name")
)
Looking at the bar with the most crimes now:
crimes_per_prem_2 |>
select(name, num_crimes) |>
arrange(desc(num_crimes))
## Simple feature collection with 132 features and 2 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: -2.256285 ymin: 53.47278 xmax: -2.221694 ymax: 53.48525
## Geodetic CRS: WGS 84
## First 10 features:
## name num_crimes geometry
## 1 Crafty Pig 50 POINT (-2.236427 53.48185)
## 2 NQ64 20 POINT (-2.24827 53.47824)
## 3 NQ64 20 POINT (-2.236719 53.48249)
## 4 Bliss Club Manchester 14 POINT (-2.247691 53.47476)
## 5 Centre Stage 13 POINT (-2.238146 53.47691)
## 6 Mala 12 POINT (-2.234975 53.48201)
## 7 Albert's Schloss 10 POINT (-2.24791 53.4782)
## 8 SIXES 9 POINT (-2.243594 53.48512)
## 9 The Salmon Of Knowledge 8 POINT (-2.233841 53.48236)
## 10 Chill#96 8 POINT (-2.240749 53.47734)
Which of the two approaches is better? it depends on the case.
Using the second approach, double-counting is avoided, at the expense of attributing the crime to the nearest bar. This too, may not be a fair assumption to make, since drunk people do illogical things.
Looking closely at Crafty Pig:
cp <- cc_bars |>
filter(name == "Crafty Pig")
cp_buffer <- bar_buffer_100 |>
filter(name == "Crafty Pig") |>
st_transform(4326)
cp_crimes <- crime_w_bars |>
filter(nearest_bar$name == "Crafty Pig")
Using mapply() and st_union() functions to
draw a line between each crime and the Crafty Pig bar:
dist_lines <- st_sfc(
mapply(
function(a, b){
st_cast(st_union(a, b), "LINESTRING") # specify function
},
cp_crimes$geometry, # input a for the function
cp_crimes$nearest_bar$geometry, # input b for the function
SIMPLIFY = FALSE # don't attempt to reduce the result
)
)
Note: the st_sfc() is used to create a simple feature
geometry list column.
Plotting:
plot(st_geometry(cp_buffer))
plot(st_geometry(cp), col = "black", add = TRUE)
plot(st_geometry(cp_crimes), col = "blue", add = TRUE)
plot(st_geometry(dist_lines), col = "green", add = TRUE)
All the crimes happened at only one location, which is within the 100m buffer. How far exactly?
cp_crimes <- cp_crimes |>
mutate(distance = st_distance(geometry, nearest_bar$geometry))
Arranging:
cp_crimes$distance[1:4]
## Units: [m]
## [1] 67.09451 67.09451 67.09451 67.09451
Why is it that all these crimes are geo-coded to one single location? This is due to geo-masking of data, to ensure anonymity.
m <- leaflet() |>
addTiles() |>
addMarkers(
lng=-2.230899, # longitude
lat=53.464987, # latitude
popup="University of Manchester"
)
m
Adding multiple points:
latitudes = c(53.464987, 53.472726, 53.466649)
longitudes = c(-2.230899, -2.245481, -2.243421)
popups = c("You are here", "Here is another point", "Here is another point")
df <- data.frame(latitudes, longitudes, popups)
m <- leaflet(data = df) |>
addTiles() |>
addMarkers(
lng = ~longitudes,
lat = ~latitudes,
popup = ~popups
)
m
Creating a color palette using colorBin():
pal <- colorBin(
"RdPu",
domain = crimes_per_prem$n, # what value to use for shading
bins = 5,
pretty = TRUE
)
Using the created color bin to color polygons that on a leaflet map:
leaflet(crimes_per_prem) |>
addTiles() |>
addPolygons(
fillColor = ~pal(n),
fillOpacity = 0.8,
weight = 1,
opacity = 1,
color = "black",
label = ~as.character(name)
) |>
addLegend(
pal = pal,
values = ~n,
opacity = 0.7,
title = "Violent crimes",
position = "bottomright"
)
Creating an address to geocode:
addresses <- data.frame(
name = "Sherlock Holmes",
address = "221B Baker Street, London, UK"
)
Using the function geocode() function from
geocoder package to get the coordinates for the address
above:
addresses |> geocode(address, method = "osm")
## Passing 1 address to the Nominatim single address geocoder
## Query completed in: 1.2 seconds
## # A tibble: 1 × 4
## name address lat long
## <chr> <chr> <dbl> <dbl>
## 1 Sherlock Holmes 221B Baker Street, London, UK 51.5 -0.158
Getting data about different alcohol outlets via the Licensed Premises dataset:
data_url <- "http://www.manchester.gov.uk/open/download/downloads/id/169/licensed_premises.csv"
lic_prem <- read_csv(data_url) |> clean_names()
## Warning: One or more parsing issues, call `problems()` on your data frame for details,
## e.g.:
## dat <- vroom(...)
## problems(dat)
## Rows: 65535 Columns: 36
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (23): EXTRACTDATE, ORGANISATIONURI, ORGANISATIONLABEL, CASEDATE, SERVICE...
## dbl (1): CASEREFERENCE
## lgl (12): LATENIGHTREFRESHMENT, ALCOHOLSUPPLY, OPENINGHOURS, LICENCEENDDATE,...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(lic_prem)
## # A tibble: 6 × 36
## extractdate organisationuri organisationlabel casereference casedate
## <chr> <chr> <chr> <dbl> <chr>
## 1 10/10/2014 http://opendatacommuniti… Manchester 1565 07/03/2…
## 2 10/10/2014 http://opendatacommuniti… Manchester 1823 25/03/2…
## 3 10/10/2014 http://opendatacommuniti… Manchester 1824 24/03/2…
## 4 10/10/2014 http://opendatacommuniti… Manchester 1826 24/03/2…
## 5 10/10/2014 http://opendatacommuniti… Manchester 1834 11/04/2…
## 6 10/10/2014 http://opendatacommuniti… Manchester 1841 07/04/2…
## # ℹ 31 more variables: servicetypeuri <chr>, servicetypelabel <chr>,
## # premisesname <chr>, locationtext <chr>, postcode <chr>,
## # entertainmenttypeuri <chr>, entertainmenttypelabel <chr>,
## # latenightrefreshment <lgl>, alcoholsupply <lgl>, openinghours <lgl>,
## # statusuri <chr>, statuslabel <chr>, licencestartdate <chr>,
## # licenceenddate <lgl>, onpremisesalcoholsale <lgl>,
## # offpremisesalcoholsale <lgl>, licenceholder <chr>, …
glimpse(lic_prem)
## Rows: 65,535
## Columns: 36
## $ extractdate <chr> "10/10/2014", "10/10/2014", "10/10/2014",…
## $ organisationuri <chr> "http://opendatacommunities.org/id/metrop…
## $ organisationlabel <chr> "Manchester", "Manchester", "Manchester",…
## $ casereference <dbl> 1565, 1823, 1824, 1826, 1834, 1841, 1854,…
## $ casedate <chr> "07/03/2005", "25/03/2005", "24/03/2005",…
## $ servicetypeuri <chr> "http://id.esd.org.uk/service/860", "http…
## $ servicetypelabel <chr> "Premises Licence", "Club Premises Certif…
## $ premisesname <chr> "Fresh & Frozen", "Didsbury Tennis Club",…
## $ locationtext <chr> "21 Assheton Road, Manchester", "509 Parr…
## $ postcode <chr> "M40 1UB", "M20 5GQ", "M4 1QB", "M22 9TF"…
## $ entertainmenttypeuri <chr> "http://id.esd.org.uk/entertainment/6", N…
## $ entertainmenttypelabel <chr> "Recorded Music", NA, "Live Music|Recorde…
## $ latenightrefreshment <lgl> TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRU…
## $ alcoholsupply <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
## $ openinghours <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ statusuri <chr> "http://id.esd.org.uk/premisesLicenceStat…
## $ statuslabel <chr> "Current", "Current", "Current", "Current…
## $ licencestartdate <chr> "05/04/2005", "23/04/2005", "22/04/2005",…
## $ licenceenddate <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ onpremisesalcoholsale <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ offpremisesalcoholsale <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ licenceholder <chr> "Mrs Rasmi Panchal", "Didsbury Lawn Tenni…
## $ licenceholderaddress <chr> "23 Assheton Road Manchester", "509 Parrs…
## $ licenceholderpostcode <chr> "M40 1UB", "M20 5GQ", "M4 1QB", "M22 9TF"…
## $ applicanttypeuri <chr> "http://id.esd.org.uk/premisesLicenceAppl…
## $ applicanttypelabel <chr> "Individual", NA, "Limited Company", NA, …
## $ designatedpremisessupervisor <chr> "Mr Mahesh C B Panchal", NA, "David Holli…
## $ supervisorlicenceno <chr> "31726", NA, "1761", NA, "PL5031", "1859"…
## $ supervisoraddress <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ supervisorpostcode <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ conditions <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ decisiondate <chr> "05/04/2005", "23/04/2005", "22/04/2005",…
## $ decidedby <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ licenceurl <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ uprn <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ geopointlicensingurl <chr> "http://www.nationalarchives.gov.uk/doc/o…
Since there are more than 65K entries here, to illustrate geocoding, we will slice the data to include only the top 50 entries, then extract the complete address to a new column, and finally, get the coordinates of the complete address.
lic_prem <- lic_prem |>
slice(1:50) |> # Select first 50 entries
mutate(complete_address = paste(locationtext, postcode, sep = ", ")) |>
geocode(complete_address, method = "osm")
## Passing 50 addresses to the Nominatim single address geocoder
## Query completed in: 63.6 seconds
Making sure that the coordinates are numeric:
glimpse(lic_prem)
## Rows: 50
## Columns: 39
## $ extractdate <chr> "10/10/2014", "10/10/2014", "10/10/2014",…
## $ organisationuri <chr> "http://opendatacommunities.org/id/metrop…
## $ organisationlabel <chr> "Manchester", "Manchester", "Manchester",…
## $ casereference <dbl> 1565, 1823, 1824, 1826, 1834, 1841, 1854,…
## $ casedate <chr> "07/03/2005", "25/03/2005", "24/03/2005",…
## $ servicetypeuri <chr> "http://id.esd.org.uk/service/860", "http…
## $ servicetypelabel <chr> "Premises Licence", "Club Premises Certif…
## $ premisesname <chr> "Fresh & Frozen", "Didsbury Tennis Club",…
## $ locationtext <chr> "21 Assheton Road, Manchester", "509 Parr…
## $ postcode <chr> "M40 1UB", "M20 5GQ", "M4 1QB", "M22 9TF"…
## $ entertainmenttypeuri <chr> "http://id.esd.org.uk/entertainment/6", N…
## $ entertainmenttypelabel <chr> "Recorded Music", NA, "Live Music|Recorde…
## $ latenightrefreshment <lgl> TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRU…
## $ alcoholsupply <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
## $ openinghours <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ statusuri <chr> "http://id.esd.org.uk/premisesLicenceStat…
## $ statuslabel <chr> "Current", "Current", "Current", "Current…
## $ licencestartdate <chr> "05/04/2005", "23/04/2005", "22/04/2005",…
## $ licenceenddate <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ onpremisesalcoholsale <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ offpremisesalcoholsale <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ licenceholder <chr> "Mrs Rasmi Panchal", "Didsbury Lawn Tenni…
## $ licenceholderaddress <chr> "23 Assheton Road Manchester", "509 Parrs…
## $ licenceholderpostcode <chr> "M40 1UB", "M20 5GQ", "M4 1QB", "M22 9TF"…
## $ applicanttypeuri <chr> "http://id.esd.org.uk/premisesLicenceAppl…
## $ applicanttypelabel <chr> "Individual", NA, "Limited Company", NA, …
## $ designatedpremisessupervisor <chr> "Mr Mahesh C B Panchal", NA, "David Holli…
## $ supervisorlicenceno <chr> "31726", NA, "1761", NA, "PL5031", "1859"…
## $ supervisoraddress <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ supervisorpostcode <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ conditions <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ decisiondate <chr> "05/04/2005", "23/04/2005", "22/04/2005",…
## $ decidedby <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ licenceurl <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ uprn <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ geopointlicensingurl <chr> "http://www.nationalarchives.gov.uk/doc/o…
## $ complete_address <chr> "21 Assheton Road, Manchester, M40 1UB", …
## $ lat <dbl> 53.49526, 53.40512, 53.48318, 53.38518, 5…
## $ long <dbl> -2.170846, -2.230231, -2.239146, -2.26143…
Creating leaflet map:
leaflet(data = lic_prem) |>
addTiles() |>
addMarkers(
lng = ~long,
lat = ~lat,
popup = ~as.character(premisesname),
label = ~as.character(premisesname)
)
## Warning in validateCoords(lng, lat, funcName): Data contains 5 rows with either
## missing or invalid lat/lon values and will be ignored
How far are police stations in Madrid?
Reading in data:
comisarias <- read_csv("data/data/nationalpolice.csv")
## Rows: 34 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): NOMBRE
## dbl (2): X, Y
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
comisarias_sf <- st_as_sf(
comisarias,
coords = c("X", "Y"),
crs = 4326
)
Creating unique IDs for each row:
comisarias_sf$id <- as.numeric(rownames(comisarias_sf))
Get boundary data for Madrid
madrid <- st_read("data/data/madrid.geojson")
## Reading layer `madrid' from data source
## `/home/norman/Documents/ThirdBrain/x1_Projects/RProjects/Notes-on-Crime-Mapping-and-Spatial-Data-Analysis-in-R/data/data/madrid.geojson'
## using driver `GeoJSON'
## Simple feature collection with 1 feature and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -3.888963 ymin: 40.31206 xmax: -3.518126 ymax: 40.64328
## Geodetic CRS: WGS 84
Plotting with leaflet:
leaflet(comisarias_sf) |>
addTiles() |>
addMarkers(data = comisarias_sf) |>
addPolygons(data = madrid)
Common distances used in spatial data science: - Euclidean distance: the length of a segment connected by 2 points in a 2d space. - great circle distance: the length of arc linking 2 points on a sphere. - Manhattan distance: distance between points along a rectilinear path (grid-like)
Calculating distance between a selected police headquarter to two other headquarters:
dist_headquarters <- st_distance(
slice(comisarias_sf, 34),
slice(comisarias_sf, c(1, 10, 25))
)
dist_headquarters
## Units: [m]
## [,1] [,2] [,3]
## [1,] 8124.085 5085.917 8554.864
Expressing the distance units in kilometers:
set_units(dist_headquarters, "km")
## Units: [km]
## [,1] [,2] [,3]
## [1,] 8.124085 5.085917 8.554864
Calculating distances between all police stations:
m_distance <- st_distance(comisarias_sf)
head(m_distance)
## Units: [m]
## [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,] 0.000 4365.495 5423.888 18592.24 2788.721 5841.085 6146.7843
## [2,] 4365.495 0.000 1325.770 14228.38 3622.821 1666.964 1937.1795
## [3,] 5423.888 1325.770 0.000 13302.89 4037.707 1830.498 1931.0537
## [4,] 18592.242 14228.381 13302.887 0.00 17237.398 12822.591 12514.8359
## [5,] 2788.721 3622.821 4037.707 17237.40 0.000 5281.130 5533.3175
## [6,] 5841.085 1666.964 1830.498 12822.59 5281.130 0.000 308.2584
## [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,] 11695.630 7909.153 10553.122 6676.909 11358.779 7601.352 11185.307
## [2,] 7833.044 3723.656 6193.266 2511.152 7700.904 3939.523 8819.637
## [3,] 7632.592 3413.614 5353.904 1257.578 7655.604 2623.228 9365.686
## [4,] 8729.769 10882.031 8041.864 12133.558 9602.549 12040.188 13447.852
## [5,] 11446.352 7281.888 9376.676 5115.122 11322.760 5486.120 12117.125
## [6,] 6167.415 2086.962 4797.362 2248.173 6058.148 4002.974 7535.317
## [,15] [,16] [,17] [,18] [,19] [,20] [,21]
## [1,] 13550.684 1435.214 5782.0229 8124.881 10810.179 8986.842 8803.567
## [2,] 9376.275 4276.341 2037.9385 3811.016 6940.378 5319.830 5180.338
## [3,] 8843.792 5052.633 2561.8204 2729.465 5626.755 3995.157 3858.226
## [4,] 6142.660 18355.518 13107.9539 10586.518 9616.043 11189.868 11384.500
## [5,] 12856.793 1540.920 5613.5210 6670.043 8644.227 6765.962 6566.732
## [6,] 7752.663 5910.732 808.9659 2894.029 6486.607 5189.104 5104.604
## [,22] [,23] [,24] [,25] [,26] [,27] [,28]
## [1,] 6574.161 6569.241 9039.032 13511.134 7332.848 9895.030 10018.527
## [2,] 2249.998 2247.377 4675.541 9191.243 3123.997 6450.696 6559.360
## [3,] 1255.085 1245.387 3842.079 8096.464 2849.886 6598.999 6692.794
## [4,] 12073.004 12080.153 9553.251 5563.385 11392.637 10882.416 10788.886
## [5,] 5290.620 5280.306 7874.576 11865.711 6683.999 10044.279 10156.276
## [6,] 1621.216 1630.395 3338.344 8041.725 1497.011 4871.694 4974.000
## [,29] [,30] [,31] [,32] [,33] [,34]
## [1,] 7407.900 5156.917 8381.809 6128.7114 18669.925 8124.085
## [2,] 6169.387 3328.854 5375.511 1768.4818 14307.231 4871.027
## [3,] 5421.308 2788.928 4150.850 1304.2686 13391.058 5242.508
## [4,] 15915.314 15100.234 12763.324 12463.5578 158.655 12120.650
## [5,] 4619.410 2589.821 5873.595 5180.9705 17333.146 8398.137
## [6,] 7251.805 4573.020 5740.168 814.0143 12892.244 3424.062
Checking the CRS used in madrid data:
st_crs(madrid)
## Coordinate Reference System:
## User input: WGS 84
## wkt:
## GEOGCRS["WGS 84",
## DATUM["World Geodetic System 1984",
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## ID["EPSG",4326]]
Reprojecting the CRS of madrid to another using
crsuggest (if we don’t know the EPSG code):
suggested_crs <- suggest_crs(madrid)
head(suggested_crs)
## # A tibble: 6 × 6
## crs_code crs_name crs_type crs_gcs crs_units crs_proj4
## <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 2062 Madrid 1870 (Madrid) / Spain LCC project… 4903 m +proj=lc…
## 2 3042 ETRS89 / UTM zone 30N (N-E) project… 4258 m +proj=ut…
## 3 25830 ETRS89 / UTM zone 30N project… 4258 m +proj=ut…
## 4 23030 ED50 / UTM zone 30N project… 4230 m +proj=ut…
## 5 32630 WGS 84 / UTM zone 30N project… 4326 m +proj=ut…
## 6 32430 WGS 72BE / UTM zone 30N project… 4324 m +proj=ut…
Transforming the CRS of madrid to the “best” suggestion
from the crsuggest package:
madrid_meters <- st_transform(madrid, crs = 2062)
head(madrid_meters)
## Simple feature collection with 1 feature and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 582948.6 ymin: 634613.4 xmax: 614351.6 ymax: 671346.7
## Projected CRS: Madrid 1870 (Madrid) / Spain LCC
## OBJECTID Shape_Leng Shape_Area TM geometry
## 1 1 174177.9 604455107 Madrid MULTIPOLYGON (((603625.2 67...
Dividing Madrid into a 250m x 250m grid:
madrid_grid <- st_make_grid(madrid_meters, cellsize = 250)
Extracting only the points in the limits of Madrid:
madrid_grid <- st_intersection(madrid_grid, madrid_meters)
Plotting:
plot(madrid_grid)
Estimating the minimum distance value from the center of each grid, to the nearest station:
comisarias_sf_meters <- st_transform(comisarias_sf, crs = 2062)
distances <- st_distance(
comisarias_sf_meters,
st_centroid(madrid_grid)
) |>
as_tibble()
head(distances)
## # A tibble: 6 × 10,082
## V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11
## [m] [m] [m] [m] [m] [m] [m] [m] [m] [m] [m]
## 1 16047. 16238. 16463. 16688. 16905. 17112. 17322. 17532. 17738. 17942. 18146.
## 2 14159. 14319. 14509. 14700. 14884. 15051. 15223. 15396. 15565. 15732. 15901.
## 3 13031. 13183. 13365. 13548. 13724. 13883. 14047. 14212. 14375. 14534. 14696.
## 4 17076. 17058. 17042. 17031. 17014. 16970. 16932. 16895. 16859. 16818. 16780.
## 5 13259. 13449. 13674. 13898. 14116. 14322. 14532. 14743. 14950. 15154. 15360.
## 6 14667. 14811. 14981. 15154. 15320. 15467. 15618. 15771. 15922. 16069. 16218.
## # ℹ 10,071 more variables: V12 [m], V13 [m], V14 [m], V15 [m], V16 [m],
## # V17 [m], V18 [m], V19 [m], V20 [m], V21 [m], V22 [m], V23 [m], V24 [m],
## # V25 [m], V26 [m], V27 [m], V28 [m], V29 [m], V30 [m], V31 [m], V32 [m],
## # V33 [m], V34 [m], V35 [m], V36 [m], V37 [m], V38 [m], V39 [m], V40 [m],
## # V41 [m], V42 [m], V43 [m], V44 [m], V45 [m], V46 [m], V47 [m], V48 [m],
## # V49 [m], V50 [m], V51 [m], V52 [m], V53 [m], V54 [m], V55 [m], V56 [m],
## # V57 [m], V58 [m], V59 [m], V60 [m], V61 [m], V62 [m], V63 [m], V64 [m], …
Processing our data in preparation for mapping in leaflet:
police_distances <- data.frame(
us = st_transform(madrid_grid, crs = 4326), # set grid to WGS 84 CRS
distance_km = map_dbl(distances, min) / 1000, # extract min. distance for each grid
location_id = map_dbl(distances, function(x) match(min(x), x))
) |> # extract the value's index
left_join(comisarias_sf, by = c("location_id" = "id"))
head(police_distances)
## geometry.x distance_km location_id
## 1 POLYGON ((-3.60848 40.31433... 8.639067 29
## 2 POLYGON ((-3.60848 40.31433... 8.829202 29
## 3 POLYGON ((-3.605535 40.3143... 9.053304 29
## 4 POLYGON ((-3.602591 40.3143... 9.277918 29
## 5 POLYGON ((-3.599646 40.3143... 9.495928 29
## 6 POLYGON ((-3.596702 40.3143... 9.703742 29
## NOMBRE
## 1 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## 2 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## 3 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## 4 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## 5 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## 6 Madrid - Usera - Villaverde 1. Comisaría tramitación de DNI y pasaporte
## geometry.y
## 1 POINT (-3.693897 40.35755)
## 2 POINT (-3.693897 40.35755)
## 3 POINT (-3.693897 40.35755)
## 4 POINT (-3.693897 40.35755)
## 5 POINT (-3.693897 40.35755)
## 6 POINT (-3.693897 40.35755)
Inspect the distribution of the calculated distances:
hist(police_distances$distance_km, main = "Distance to nearest police station")
More preparation for mapping: creating a custom icon for plotting data points…
icon_url_pt1 <- "https://upload.wikimedia.org/wikipedia/commons/"
icon_url_pt2 <- "a/ad/189-woman-police-officer-1.svg"
# creating the icon
police_icon <- makeIcon(
paste0(icon_url_pt1, icon_url_pt2),
iconWidth = 12,
iconHeight = 20
)
Creating a color scale for plotting:
bins <- quantile(police_distances$distance_km)
pal <- colorBin(
c("#0868AC", "#43A2CA", "#7BCCC4", "#BAE4BC", "#F0F9E8"),
domain = police_distances$distance_km,
bins = bins,
reverse = TRUE
)
Finally creating the leaflet map:
full_map <- leaflet() |>
addTiles() |>
addMarkers(
data = comisarias_sf,
icon = ~police_icon,
group = "Police stations"
) |>
addPolygons(
data = police_distances[[1]],
fillColor = pal(police_distances$distance_km),
fillOpacity = 0.8,
weight = 0,
opacity = 1,
color = "transparent",
group = "Distances",
highlight = highlightOptions(
weight = 2, color = "#666",
bringToFront = TRUE, opacity = 1
),
popupOptions = popupOptions(
autoPan = FALSE,
closeOnClick = TRUE,
textOnly = TRUE
)
) |>
addLegend(
pal = pal,
values = police_distances$distance_km,
opacity = 0.8,
title = "Distance (Km)",
position = "bottomright"
) |>
addScaleBar(position = "bottomleft")
full_map